﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Windows;
using System.Xml.Serialization;
using System.IO;

namespace nGSO
{
    /// <summary>
    /// Algorithm environment search space for agents distributing
    /// </summary>
    [XmlRoot("space")]
    public class SearchSpace
    {
        public readonly ObjectiveFunction AgentObjectiveFun;

        [XmlElement("config")]
        protected GsoConfig _config {get;set;}
        
        [XmlArray("agents")]
        public List<Agent> Agents { get; set; }

        public SearchSpace() { } // only for xml serialzier

        public SearchSpace(GsoConfig config)
        {
            _config = config;
            Agents = new List<Agent>();

            AgentObjectiveFun += new ObjectiveFunction(GsoConfig.FitnessFunPointer.EvaluateMultimodalFunction);           
        }
        public SearchSpace(GsoConfig config, ObjectiveFunction fitnessFun)
        {
            _config = config;
            Agents = new List<Agent>();
            AgentObjectiveFun += fitnessFun;
        }


        /// <summary>
        /// Deploy agents randomly, init agent defaults
        /// </summary>
        public void InitAgents()
        {
            Debug.WriteLine("Randomly distributing agents");
            Random rand = new Random();

            for (int i = 0; i < _config.Population; i++)
            {
                Agent agent = GetAgent();
                
                agent.Luciferin = _config.DefaultLuminescence;
                agent.NeighborhoodRange = _config.RadialSensorRange; 

                SetInitialAgentLocation(rand, agent);
               
                Agents.Add(agent);

                Debug.WriteLine("Agent was added to search space: " + agent.ToString());
            }
        }

        protected virtual Agent GetAgent()
        {
            //objective function powinna byc atrybutem przestrzeniu wyszukiwania anie agenta
            
            Agent agent = new Agent();
            return agent;
        }

        protected virtual void SetInitialAgentLocation(Random rand, Agent agent)
        {
            agent.Loc = new double[_config.Dims];

            for (int j = 0; j < _config.Dims; j++)
            {
                agent.Loc[j] = rand.NextDouble() * _config.SpaceSize/2 * (rand.NextDouble() > 0.5 ? 1 : -1);
            }
        }

        public virtual void MoveAgent(Agent agent, Agent agentToMoveToward)
        {
            // a moves to b
            double[] aLoc = agent.Loc;
            double[] bLoc = agentToMoveToward.Loc;

            var vec = new double[_config.Dims];
            for (int i = 0; i < _config.Dims; i++)
            {
                vec[i] = bLoc[i] - aLoc[i];
            }

            double tmpSum = 0;
            for (int i = 0; i < _config.Dims; i++)
            {
                tmpSum += Math.Pow(vec[i], 2);
            }
            double len = Math.Sqrt(tmpSum);

            var unitVec = new double[_config.Dims];
            for (int i = 0; i < _config.Dims; i++)
            {
                unitVec[i] = vec[i] / len;
            }

            double[] newPos = new double[_config.Dims];
            for (int i = 0; i < _config.Dims; i++)
            {
                newPos[i] = aLoc[i] + unitVec[i] * _config.StepSize;
            }

            agent.NextLoc = newPos;
        }
    }

    public static class AgentGroupsExtensions
    {
        public static bool IsInGroup(this List<List<Guid>> groupList, Agent agent)
        {
            foreach (var group in groupList)
            {
                if (group.Contains(agent.Id))
                    return true;
            }
            return false;
        }
    }
}